#include <cstddef>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>
#include <string_view>
#include <typeinfo>
#include <string.h>
#include <memory>
#include <fstream>
#include <cmath>
#include <set>
#include <variant>
#include "defer.h"
#include "jsonObject.h"
using namespace std;

std::string JsonObject::replace(std::string s, std::string old_s, std::string new_s, int n)
{
    int pre = 0, now;
    string res = "";
    while (pre < s.length() && (now = s.find(old_s, pre)) != -1 && n)
    {
        res += s.substr(pre, now - pre);
        res += new_s;
        pre = now + old_s.length();
        n--;
    }
    res += s.substr(pre);
    return res;
}

JsonObjectPtr JsonObject::copy(const JsonObject &jo)
{
    return JSONOBJECT(jo);
}

JsonObject::JsonObject() : json_obj() {}
JsonObject::JsonObject(void*) : json_obj(nullptr) {}
JsonObject::JsonObject(std::nullptr_t) : json_obj(nullptr) {}
JsonObject::JsonObject(int pi) : json_obj(pi){}
JsonObject::JsonObject(long long pl) : json_obj(pl) {}
JsonObject::JsonObject(const std::string& ps) : json_obj(ps) {}
JsonObject::JsonObject(const char *ps) : json_obj(string(ps)) {}
JsonObject::JsonObject(double pd) : json_obj(pd) {}
JsonObject::JsonObject(bool pb) : json_obj(pb) {}
JsonObject::JsonObject(const JsonDict& pt) : json_obj(pt) {}
JsonObject::JsonObject(const JsonArray& pv) : json_obj(pv) {}

ObjectType JsonObject::getType() const { return static_cast<ObjectType>(json_obj.index()); }

string_view JsonObject::getTypeStr() const {
    auto get_obj_name = [] (auto x) -> string_view {
        return object_name<static_cast<ObjectType>(get_type_index_v<decltype(x), json_obj_t>)>;
    };
    return std::visit(get_obj_name, json_obj); 
}

const long long& JsonObject::asInt() const
{
    return std::get<long long>(json_obj);
}
const double& JsonObject::asDouble() const
{
    return std::get<double>(json_obj);
}
const std::string& JsonObject::asString() const
{
    return std::get<string>(json_obj);
}
const JsonDict& JsonObject::asDict() const
{
    return std::get<JsonDict>(json_obj);
}
const JsonArray& JsonObject::asArray() const
{
    return std::get<JsonArray>(json_obj);
}
const bool& JsonObject::asBool() const
{
    return std::get<bool>(json_obj);
}

long long& JsonObject::asInt()
{
    return std::get<long long>(json_obj);
}
double& JsonObject::asDouble()
{
    return std::get<double>(json_obj);
}
std::string& JsonObject::asString()
{
    return std::get<string>(json_obj);
}
JsonDict& JsonObject::asDict()
{
    return std::get<JsonDict>(json_obj);
}
JsonArray& JsonObject::asArray()
{
    return std::get<JsonArray>(json_obj);
}
bool& JsonObject::asBool()
{
    return std::get<bool>(json_obj);
}
std::nullptr_t JsonObject::asNullptr()
{
    return std::get<nullptr_t>(json_obj);
}


long long JsonObject::getInt() const
{
    return std::get<long long>(json_obj);
}
double JsonObject::getDouble() const
{
    return std::get<double>(json_obj);
}
std::string JsonObject::getString() const
{
    return std::get<string>(json_obj);
}
JsonDict JsonObject::getDict() const
{
    return std::get<JsonDict>(json_obj);
}
JsonArray JsonObject::getArray() const
{
    return std::get<JsonArray>(json_obj);
}
bool JsonObject::getBool() const
{
    return std::get<bool>(json_obj);
}
nullptr_t JsonObject::getNullptr() const
{
    return std::get<nullptr_t>(json_obj);
}

std::string JsonObject::convert(bool reverse) const
{
    stringstream ss;
    int type = json_obj.index();
    switch (type)
    {
    case JSONDOUBLE:
        ss << get<double>(json_obj);
        break;
    case JSONINT:
        ss << get<long long>(json_obj);
        break;
    case JSONBOOL:
        ss << (get<bool>(json_obj) ? "true" : "false");
        break;
    case JSONNULLPTR:
        ss << "null";
        break;
    case JSONSTRING:
        ss << get<std::string>(json_obj);
        break;
    case JSONINVALID:
        throw "invalid json";
    default:
        break;
    }
    string s = ss.str();
    if (type == JSONSTRING)
    {
        if (reverse)
        {
            set<char> convert_str = {'\\', 'r', 'n', '"', 'b', 'f', 't', '/'};
            int index = 0, len = s.length();
            for (int i = 0; i < len; i++, index++) {
                if (s[i] == '\\') {
                    if (i < len - 1 && convert_str.find(s[i + 1]) != convert_str.end()) {
                        s[index] = s[i + 1];
                        i ++;
                    } else {
                        throw "invalid json";
                    }
                } else {
                    s[index] = s[i];
                }
            }
            s.resize(index);
        }
        else
        {
            s = replace(s, "\\", "\\\\"); //将反斜杠转为反斜杠反斜杠
            s = replace(s, "\"", "\\\""); //将双引号转为反斜杠双引号
            s = replace(s, "\r", "\\r");
            s = replace(s, "\n", "\\n");
            s = replace(s, "\b", "\\b");
            s = replace(s, "\f", "\\f");
            s = replace(s, "\t", "\\t");
            s = replace(s, "/", "\\/");
            s = "\"" + s + "\"";
        }
    }
    return s;
}

std::string JsonObject::convert(std::string s, bool reverse)
{
    if (reverse)
    {
        set<char> convert_str = {'\\', 'r', 'n', '"', 'b', 'f', 't', '/'};
        int index = 0, len = s.length();
        for (int i = 0; i < len; i++, index++) {
            if (s[i] == '\\') {
                if (i < len - 1 && convert_str.find(s[i + 1]) != convert_str.end()) {
                    s[index] = s[i + 1];
                    i ++;
                } else {
                    throw "invalid json";
                }
            } else {
                s[index] = s[i];
            }
        }
        s.resize(index);
    }
    else
    {
        s = replace(s, "\\", "\\\\"); //将反斜杠转为反斜杠反斜杠
        s = replace(s, "\"", "\\\""); //将双引号转为反斜杠双引号
        s = replace(s, "\r", "\\r");
        s = replace(s, "\n", "\\n");
        s = replace(s, "\b", "\\b");
        s = replace(s, "\f", "\\f");
        s = replace(s, "\t", "\\t");
        s = replace(s, "/", "\\/");
        s = "\"" + s + "\"";
    }
    return s;
}

std::string JsonObject::json() const
{
    string res = "";
    int type = json_obj.index();
    switch (type)
    {
    case JSONDOUBLE:
    case JSONINT:
    case JSONBOOL:
    case JSONNULLPTR:
    case JSONSTRING:
        return convert();
        break;
    case JSONDICT:
        for (pair<string, JsonObjectPtr > p : get<JsonDict>(json_obj))
        {
            if (res != "")
                res += ",";
            res += JsonObject(p.first).convert() + ":" + p.second->json();
        }
        return "{" + res + "}";
        break;
    case JSONARRAY:
        for (JsonObjectPtr p : get<JsonArray>(json_obj))
        {
            if (res != "")
                res += ",";
            res += p->json();
        }
        return "[" + res + "]";
        break;
    case JSONINVALID:
        return "invalid json";
    default:
        return "";
    }
}

JsonObjectPtr JsonObject::decoder(const std::string &s)
{
    if (s.length() == 0)
        return JSONOBJECT();
    try
    {
        int sta = 0, end = s.length();
        return findNextJsonObeject(s, sta, end);
        // JsonObject* temp = findNextDict(s, sta, end);
        // JsonObject res = *temp;
        // delete temp;
        // return res;
    }
    catch (char *s)
    {
        // cout << "catch error char*" << endl;
        // cout << "msg:" << s << endl;
        delete s;
        return JSONOBJECT();
    }
    catch (const char *s)
    {
        // cout << "catch error const char*" << endl;
        // cout << "msg:" << s << endl;
        return JSONOBJECT();
    }
    catch (string s)
    {
        // cout << "catch error string" << endl;
        // cout << "size:" << s.length() << "msg:" << s << endl;
        return JSONOBJECT();
    }
}

int JsonObject::firstNonEmpty(const std::string &s, int sta, int end, int step, bool flag)
{
    while (sta < end && (isspace(s[sta]) > 0) == flag)
        sta++;
    return sta;
}

int JsonObject::skipEmptyOneChar(const std::string &s, int sta, int end, char c)
{
    bool flag = false;
    while (sta < end)
    {
        if (!isspace(s[sta]))
        {
            if (s[sta] == c)
            {
                // cout << sta << " " << s[sta] << " " << flag << endl;
                if (!flag)
                    flag = true;
                else
                    break;
            }
            else
            {
                break;
            }
        }
        sta++;
    }
    return sta;
}

void JsonObject::reportError(const char *s, int x, int y)
{
    char *res = new char[100];
    if (y == -1)
        sprintf(res, s, x);
    else
        sprintf(res, s, x, y);
    // cout << "error msg:" << res << endl;
    throw res;
}

JsonObjectPtr JsonObject::findNextJsonObeject(const std::string &s, int &sta, int &end)
{
    int now = firstNonEmpty(s, sta, end);
    sta = now;
    if (now >= end)
        return JSONOBJECT();
    char c = s[sta];
    // cout << "start char:" << c << endl;
    JsonObjectPtr temp = nullptr;
    if (c == '{')
        return findNextDict(s, sta, end);
    else if (c == '[')
        return findNextArray(s, sta, end);
    else if (c == '\"')
        return findNextString(s, sta, end);
    else if (c == '-' || c >= '0' && c <= '9')
        return findNextNumber(s, sta, end);
    else if (c == 't' || c == 'f')
        return findNextBool(s, sta, end);
    else if (c == 'n')
        return findNextNullptr(s, sta, end);
    else
        // throw "invalid json";
        return JSONOBJECT();
    // if (temp->getType() == JSONINVALID)
    // {
    //     return
    reportError("type error:jsonobject,invalid json in char:{%d->%d}", sta, end);
    // }
    // cout << temp->getTypeStr() << endl;
    return temp;
}

JsonObjectPtr JsonObject::findNextNumber(const std::string &s, int &sta, int &end)
{
    // cout << "number" << endl;
    int now = firstNonEmpty(s, sta, end);
    if (now >= end)
        return JSONOBJECT();
    long long l;
    double d;
    char *pos;
    string temp_s = s.substr(sta, min(end - sta, 30));
    const char *ts = temp_s.c_str();
    // cout << ts << endl;
    d = strtod(ts, &pos);
    // cout << d << endl;
    int k = pos - ts;
    // cout << "number size:" << k << endl;
    if (k == 0)
        return JSONOBJECT();
    bool onlyDigit = true;
    for (int i = 0; i < k; i++)
        if (s[sta + i] != '-' && !isdigit(s[sta + i]))
        {
            onlyDigit = false;
            break;
        }
    sta += k;
    //纯数字且长度小于十八位考虑将其转为long long
    if (onlyDigit && k + 1 < 18)
    {
        l = strtoll(ts, &pos, 0);
        return JSONOBJECT(l);
    }
    else
    {
        return JSONOBJECT(d);
    }
}
JsonObjectPtr JsonObject::findNextBool(const std::string &s, int &sta, int &end)
{
    // cout << "bool" << endl;
    int now = firstNonEmpty(s, sta, end);
    if (now >= end)
        return JSONOBJECT();
    if (end - sta >= 4 && s.substr(sta, 4) == "true")
    {
        sta += 4;
        return JSONOBJECT(true);
    }
    else if (end - sta >= 5 && s.substr(sta, 5) == "false")
    {
        sta += 5;
        return JSONOBJECT(false);
    }
    return JSONOBJECT();
}
JsonObjectPtr JsonObject::findNextArray(const std::string &s, int &sta, int &end)
{
    int now = firstNonEmpty(s, sta, end);
    if (now >= end) {
        // cout << "end point" << endl;
        return JSONOBJECT();
    }
    sta = now;
    if (s[now] != '[') {
        // cout << "array begin char is not '[', unkown char'" << s[now] << "'" << endl;
        return JSONOBJECT();
    }
    sta++;
    JsonObjectPtr temp;
    JsonArray vj;
    while (true)
    {
        sta = firstNonEmpty(s, sta, end);
        // cout << "array element begin:" << sta << endl;
        if (sta == end)
        {
            // cout << "end point" << endl;
            for (JsonObjectPtr i : vj)
            {
                // delete i;
                i.reset();
            }
            return JSONOBJECT();
            reportError("type error:array,invalid json in char:{%d->%d}", sta, end);
        }
        if (s[sta] == ']')
        {
            sta++;
            break;
        }
        temp = findNextJsonObeject(s, sta, end);
        // cout << "array element:" << temp->getType() << endl;
        // cout << "array element:" << temp->getTypeStr() << endl;
        if (temp->getType() == JSONINVALID)
        {
            // cout << "" << endl;
            for (JsonObjectPtr i : vj)
            {
                // delete i;
                i.reset();
            }
            return temp;
            reportError("type error:array,invalid json in char:{%d->%d}", sta, end);
        }
        // JsonObjectPtr value = JSONOBJECT(temp);
        // vj.push_back(value);
        vj.push_back(temp);
        // cout << "array size:" << vj.size() << endl;
        sta = skipEmptyOneChar(s, sta, end, ',');
    }
    return JSONOBJECT(vj);
}
JsonObjectPtr JsonObject::findNextNullptr(const std::string &s, int &sta, int &end)
{
    int now = firstNonEmpty(s, sta, end);
    sta = now;
    if (now >= end)
        return JSONOBJECT();
    if (end - sta >= 4 && s.substr(sta, 4) == "null")
    {
        sta += 4;
        return JSONOBJECT(nullptr);
    }
    return JSONOBJECT();
}

JsonObjectPtr JsonObject::parseDictStr(const std::string &s)
{
    int sta = 0, end = s.length();
    return findNextDict(s, sta, end);
}

JsonObjectPtr JsonObject::findNextDict(const std::string &s, int &sta, int &end)
{
    int now = firstNonEmpty(s, sta, end);
    if (now >= end)
        return JSONOBJECT();
    sta = now;
    // cout << sta << " " << end << endl;
    if (s[now] != '{')
        return JSONOBJECT();
    sta++;
    JsonObjectPtr temp;
    JsonDict mp;
    while (true)
    {
        sta = firstNonEmpty(s, sta, end);
        // cout << "dict range:" << sta << " " << end << endl;
        if (sta >= end)
        {
            for (pair<string, JsonObjectPtr > p : mp)
            {
                // delete p.second;
                p.second.reset();
            }
            return JSONOBJECT();
            reportError("type error:dict,invalid json in char:{%d->%d}", sta, end);
        }
        if (s[sta] == '}')
        {
            sta++;
            break;
        }
        // cout << "find string key" << endl;
        temp = findNextString(s, sta, end);
        if (temp->getType() == JSONINVALID)
        {
            for (pair<string, JsonObjectPtr > p : mp)
            {
                // delete p.second;
                p.second.reset();
            }
            return temp;
            reportError("type error:dict,invalid json in char:{%d->%d}", sta, end);
        }
        // cout << "get key:" << temp->asString() << endl;
        string key = temp->asString();
        // delete temp;
        temp.reset();
        sta = skipEmptyOneChar(s, sta, end, ':');
        // cout << "find value" << endl;
        temp = findNextJsonObeject(s, sta, end);
        // cout << "get value:" << temp->getTypeStr() << endl;
        // cout << temp->json() << endl;
        if (temp->getType() == JSONINVALID)
        {
            for (pair<string, JsonObjectPtr > p : mp)
            {
                // delete p.second;
                p.second.reset();
            }
            return temp;
            reportError("type error:dict,invalid json in char:{%d->%d}", sta, end);
        }
        mp[key] = temp;
        // cout << "end dict range:" << sta << " " << end << endl;
        sta = skipEmptyOneChar(s, sta, end, ',');
    }
    return JSONOBJECT(mp);
}
JsonObjectPtr JsonObject::findNextString(const std::string &s, int &sta, int &end)
{
    // cout << "string" << endl;
    int now = firstNonEmpty(s, sta, end);
    // cout << "string:" << now << "|" << s[now] << endl;
    if (now >= end)
        return JSONOBJECT();
    sta = now;
    if (s[sta] != '\"')
    {
        // printf("invalid char : %c\n", s[sta]);
        // cout << s.substr(sta) << endl;
        return JSONOBJECT();
        reportError("type error:string,invalid json in char:{%d->%d}", sta, end);
    }
    int l = now + 1, r = l;
    while (r < end)
    {
        if (s[r] == '\"' && s[r - 1] != '\\')
            break;
        r++;
    }
    if (r >= end) {
        return JSONOBJECT();
        reportError("type error:string,invalid json in char:{%d->%d}", sta, end);
    }
    // cout << "string range:" << l << " " << r << endl;
    sta = r + 1;
    return JSONOBJECT(s.substr(l, r - l));
}

bool JsonObject::isNumber() const { return json_obj.index() == JSONDOUBLE || json_obj.index() == JSONINT; }

// 转为double类型的object,若不是数字类型则返回JSONINVALID
JsonObject JsonObject::convertNumber() const
{
    int type = json_obj.index();
    if (type == JSONDOUBLE) {
        return JsonObject(this->asDouble());
    } else if (type == JSONINT) {
        return JsonObject((double)this->asInt());
    }
    return JsonObject();
}

// 增加比较是否相等的函数用于进行对象比较
bool JsonObject::equal(const JsonObject& jo, double eps, bool order) const
{
    int type = json_obj.index();
    if (type != jo.getType()) {
        if (eps >= 0 && this->isNumber() && jo.isNumber()) {
            return std::abs(this->convertNumber().asDouble() - jo.convertNumber().asDouble()) < eps;
        }
        return false;
    }
    switch (type)
    {
    case JSONDOUBLE:
        if (eps >= 0) {
            return std::abs(this->asDouble() - jo.asDouble()) < eps;
        } else {
            return this->asDouble() == jo.asDouble();
        }
        break;
    case JSONINT:
        return this->asInt() == jo.asInt();
    case JSONBOOL:
        return this->asBool() == jo.asBool();
    case JSONNULLPTR:
        return true;
    case JSONSTRING:
        return this->asString() == jo.asString();
        break;
    case JSONDICT:
    {
        const JsonDict& dict = jo.asDict();
        if (this->asDict().size() != dict.size())
            return false;
        for (auto p : this->asDict())
        {
            auto q = dict.find(p.first);
            if (q != dict.end())
            {
                if(!p.second->equal(*(q->second), eps, order))
                    return false;
            }
            else 
                return false;
        }
        return true;
    }
    case JSONARRAY:
    {        
        const JsonArray& arr = jo.asArray();
        if (this->asArray().size() != arr.size())
            return false;
        if (order) {
            for (int i = 0; i < this->asArray().size();i++)
            {
                if (!this->asArray()[i]->equal(*(arr[i]), eps, order))
                    return false;
            }
        } else {
            int len = arr.size();
            vector<bool> arr_flags(len);
            for (int i = 0; i < len; i++)
            {
                for (int j = 0; j < len; j++)
                {
                    if (!arr_flags[j]) {
                        if (this->asArray()[i]->equal(*(arr[j]), eps, true)){
                            arr_flags[j] = true;
                        }
                    }
                }
            }
            for (int i = 0; i < len; i++)
            {
                if (!arr_flags[i]) {
                    return false;
                }
            }
        }
        return true;
    }
    default:
        // 两个invalid参数一定为false
        return false;
        break;
    }
    return true;
}